class Router {
    constructor(arg) {
        if (arg && arg.constructor !== Object) {
            return console.error(`Routing configuration must be an Object`);
        }
        Router.$root = this;
    }

    // 路由跳转方法映射
    routeMap = {
        push: 'navigateTo',
        replace: 'redirectTo',
        replaceAll: 'reLaunch',
        pushTab: 'switchTab',
    };

    /**
     * 执行路由跳转
     */
    _pushTo() {
        return new Promise((resolve, reject) => {
            let { page, params, method } = this.tempRoute;
            // 对首次进入页面执行路由守卫时如果放行，method page params都为空 此时应该直接中断流程，无需抛出异常
            if (!method && !page && !params) {
                return;
            }

            let urlParams = '?';
            if (!page) {
                reject(new Error('参数page未填写'));
                return;
            } else if (params && typeof params === 'object') {
                // 处理参数，转换为url字符串
                Object.keys(params).forEach((k) => {
                    // 深度对象转为json字符串（包含数组）
                    if (typeof params[k] === 'object') {
                        if (params[k]) {
                            const json = JSON.stringify(params[k]);
                            urlParams += `${k}=${json}&`;
                        } else {
                            urlParams += `${k}=&`;
                        }
                    } else if (typeof params[k] === 'number' || typeof params[k] === 'string' || typeof params[k] === 'boolean') {
                        // 基础值直接写入
                        urlParams += `${k}=${params[k]}&`;
                    } else if (typeof params[k] === 'undefined') {
                        urlParams += `${k}=&`;
                    }
                });
            }

            // 参数组装
            if (urlParams.length === 1) {
                urlParams = '';
            } else {
                urlParams = urlParams.substr(0, urlParams.length - 1);
            }

            // 设置路由跳转方式
            if (!method) {
                method = 'navigateTo';
            }
            if (this.routeMap[method]) {
                method = this.routeMap[method];
            }

            // 调用系统跳转方法
            uni[method]({
                url: page + urlParams,
                success: () => {
                    // 执行路由后置守卫
                    if (this._afterEach && typeof this._afterEach === 'function') {
                        this._afterEach.call(this, this.tempRoute, this.route);
                    }

                    // 更新路由信息
                    this.route = {
                        path: page + urlParams,
                        params: params || {},
                        page,
                    };
                    this.tempRoute = null;
                    resolve();
                },
                fail: (e) => {
                    reject(new Error('路由跳转失败！'));
                },
            });
        });
    }

    /**动态的导航到一个新 URL 保留浏览历史
     * navigateTo
     * @param {Object} rule
     */
    push(arg) {
        const rule = {
            method: 'navigateTo',
        };
        if (typeof arg === 'string') {
            rule.page = arg;
        } else if (typeof arg === 'object') {
            rule.page = arg.page || arg.path;
            rule.params = arg.params;
        }
        this.next(rule);
    }

    /**动态的导航到一个新 URL 关闭当前页面，跳转到的某个页面。
     * redirectTo
     * @param {Object} rule
     */
    replace(arg) {
        const rule = {
            method: 'redirectTo',
        };
        if (typeof arg === 'string') {
            rule.page = arg;
        } else if (typeof arg === 'object') {
            rule.page = arg.page || arg.path;
            rule.params = arg.params;
        }
        this.next(rule);
    }

    /**动态的导航到一个新 URL 关闭所有页面，打开到应用内的某个页面
     * 	reLaunch
     * @param {Object} rule
     */
    replaceAll(arg) {
        const rule = {
            method: 'reLaunch',
        };
        if (typeof arg === 'string') {
            rule.page = arg;
        } else if (typeof arg === 'object') {
            rule.page = arg.page || arg.path;
            rule.params = arg.params;
        }
        this.next(rule);
    }

    /** 跳转Tabbar
     * 	switchTab
     * @param {Object} rule
     */
    pushTab(arg) {
        const rule = {
            method: 'switchTab',
        };
        if (typeof arg === 'string') {
            rule.page = arg;
        } else if (typeof arg === 'object') {
            rule.page = arg.page || arg.path;
            rule.params = arg.params;
        }
        this.next(rule);
    }

    /**
     * 返回到指定层级页面上
     */
    back(delta = 1) {
        // 返回上级
        if (delta.constructor != Number) {
            this._errorHandler(new Error('返回层级参数必须是一个Number类型且必须大于0：'));
            return;
        }
        uni.navigateBack({
            delta,
        });
    }

    /**
     * 分发路由
     * @param {Object} args
     */
    _next() {
        return new Promise((resolve, reject) => {
            if (this._beforeEach && typeof this._beforeEach === 'function') {
                // 需要传给守卫 to from next
                this._beforeEach.call(this, this.tempRoute, this.route, resolve);
            } else {
                this._pushTo().catch((e) => {
                    reject(e);
                });
            }
        });
    }

    next(args) {
        if (args) {
            // 保存临时数据
            if (typeof args === 'object') {
                this.tempRoute = {
                    // 第一次调用next一定存在method，后续循环调用可能不会存在，不存在时使用上次缓存的method
                    method: args.method || this.tempRoute.method,
                    page: args.page,
                    params: args.params,
                };
            } else if (typeof args === 'string') {
                this.tempRoute = {
                    page: args,
                };
            } else if (!args) {
                // 中断路由 args = false
                this.tempRoute = null;
                return;
            }

            if (!this.route && getCurrentPages() && getCurrentPages().length > 0) {
                this.route = {
                    page: '/' + getCurrentPages()[0].route,
                };
            }

            this._next()
                .then((args) => {
                    this.next(args);
                })
                .catch((e) => {
                    this.tempRoute = null;
                    this._errorHandler(e);
                });
        } else {
            this._pushTo().catch((e) => {
                this.tempRoute = null;
                this._errorHandler(e);
            });
        }
    }

    /**
     * 应用启动时执行一次路由检查（前置守卫，若通过则不做事情）
     */
    doBeforeHooks() {
        this.tempRoute = {};
        this.next({});
    }

    // 设置路由前置/后置守卫
    beforeEach(fn) {
        this._beforeEach = fn;
    }
    afterEach(fn) {
        this._afterEach = fn;
    }
    // 设置路由跳转错误处理
    onError(fn) {
        if (fn && typeof fn === 'function') {
            this._errorHandler = fn;
        }
    }

    // 获取当前路由信息
    getCurrentRoute() {
        return this.route;
    }
}

// 路由对象属性定义
Router.$root = null;
// 当前路由内容
Router.route = null;
// 临时路由信息
Router.tempRoute = null;
// 路由前置后置守卫
Router._beforeEach = null;
Router._afterEach = null;
// 路由跳转错误处理
Router._errorHandler = function (e) {
    console.error(e);
};

Router.install = function (Vue) {
    Vue.mixin({
        onLaunch: function () {},
        onLoad: function (props) {
            // 首次进入页面时,缓存中不存在当前路由信息，需要初始化路由信息
            if (!Router.$root.getCurrentRoute()) {
                const rt = {
                    params: {},
                    page: '/' + getCurrentPages()[0].route,
                };
                if (props) {
                    Object.keys(props).forEach((k) => {
                        // url转的对象全部都是字符串，需要识别其中的对象和基本数据类型
                        try {
                            const obj = JSON.parse(props[k]);
                            if (typeof obj === 'string') {
                                // 只有字符串还会是字符串，数字、布尔、数组均会转换为正常类型
                                rt.params[k] = props[k];
                            } else {
                                rt.params[k] = obj;
                            }
                        } catch (e) {
                            rt.params[k] = props[k];
                        }
                    });
                }
                Router.$root.route = rt;

                // 执行路由前置守卫
                Router.$root.doBeforeHooks();
            }

            // 自动获取页面标题(app端可能获取不到)
            const pages = getCurrentPages();
            let pageTitle = pages[pages.length - 1].pageTitle;
            // #ifdef H5
            if (!pageTitle) {
                pageTitle = document.title;
            }
            // #endif
            Router.$root.route.pageTitle = pageTitle;
        },

        onShow() {
            if (!getCurrentPages().length) {
                return;
            }
            // 获取当前路由信息
            const pages = getCurrentPages();
            const page = pages[pages.length - 1];
            let pageTitle = page.pageTitle;
            const rt = {
                params: {},
                page: '/' + page.route,
            };
            if (!pageTitle) {
                // #ifdef H5
                rt.pageTitle = document.title;
                // #endif
            } else {
                rt.pageTitle = pageTitle;
            }

            const route = Router.$root.route;
            // 若当前页面地址不等于缓存中地址,则更新缓存路由信息
            if (!route || route.page !== rt.page) {
                Router.$root.route = rt;
            }
        },
    });
    Object.defineProperty(Vue.prototype, '$Router', {
        get: function () {
            return Router.$root;
        },
    });
    Object.defineProperty(Vue.prototype, '$Route', {
        get: function () {
            return Router.$root.getCurrentRoute();
        },
    });
};

export default Router;
